/* 使用泛型变量 */
// T K U...
// function loggingIdentity<T>(arg: T): T {
//     // console.log(arg.length);  // Error: T doesn't have .length
//     return arg;
// }

/*  */
// function loggingIdentity<T>(arg: T[]): T[] {
//     console.log(arg.length);  // Array has a .length, so no more error
//     return arg;
// }
// console.log(loggingIdentity<string>(['a','b']));

// function loggingIdentity<T>(arg: Array<T>): Array<T> {
//     console.log(arg.length);  // Array has a .length, so no more error
//     return arg;
// }

/* 泛型类型 */
// (arg:number)=>number
// (arg:string)=>string
// <T>(arg:T)=>T
// function identity<T>(arg: T): T {
//     return arg;
// }
// function identity2<T>(arg: T): string {
//     return "arg";
// }
// let myIdentity: <T>(arg: T) => T;
// myIdentity = identity
// myIdentity = identity2

/* 我们也可以使用不同的泛型参数名，只要在数量上和使用方式上能对应上就可以 */
// function identity<T>(arg: T): T {
//     return arg;
// }
// let myIdentity: <U>(arg: U) => U  = identity;

/*  */
// function identity<T>(arg: T): T {
//     return arg;
// }
// let myIdentity: { <T>(arg: T): T } = identity;

/* 泛型接口 */
// interface GenericIdentityFn {
//     // name:string,
//     // fn:(arg:number)=>string,
//     <T>(arg: T): T;
// }
// function identity<T>(arg: T): T {
//     return arg;
// }
// let myIdentity: GenericIdentityFn = identity;

/* 将泛型定在在接口上 */
// interface GenericIdentityFn<T> {
//     // name:T,
//     (arg: T): T;
// }
// // interface GenericIdentityFn<T,U> {
// //     // name:T,
// //     (arg: T): T;
// //     (arg: U): U;
// // }

// function identity<T>(arg: T): T {
//     return arg;
// }

// let myIdentity: GenericIdentityFn<number> = identity;

/* 泛型类 */
// class GenericNumber<T> {
//     zeroValue: T;
//     add: (x: T, y: T) => T;
// }

// let myGenericNumber = new GenericNumber<number>();
// myGenericNumber.zeroValue = 0;
// myGenericNumber.add = function(x, y) { return x + y; };

// let stringNumeric = new GenericNumber<string>();
// stringNumeric.zeroValue = "";
// stringNumeric.add = function(x, y) { return x + y; };

// alert(stringNumeric.add(stringNumeric.zeroValue, "test"));

/* 泛型约束 */
// interface Lengthwise {
//     length: number;
// }

// function loggingIdentity<T extends Lengthwise>(arg: T): T {
//     console.log(arg.length);  // Now we know it has a .length property, so no more error
//     return arg;
// }

// // loggingIdentity<number>(3);  // Error, number doesn't have a .length property

// loggingIdentity({length: 10, value: 3});

/* 在泛型约束中使用类型参数 */
// function getProperty<T, K extends keyof T>(obj: T, key: K) {
//     return obj[key];
// }

// let x = { a: 1, b: 2, c: 3, d: 4 };
// getProperty(x, "a"); // okay
// getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.

/* 在泛型里使用类类型 */
class BeeKeeper {
    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

// interface InterFn<T> {
//     new (): T
// }
function create<T>(c: {new (): T}): T {
    return new c();
}

function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}

createInstance(Lion).keeper.nametag;  // typechecks!
createInstance(Bee).keeper.hasMask;   // typechecks!
// createInstance(ZooKeeper).keeper.hasMask;   // error!
